package fido;


import fido.exceptions.DownloadException;
import fido.torrentsite.CookiedTorrentSite;
import fido.torrentsite.TorrentSite;
import org.apache.log4j.Logger;
import org.mymediadb.api.mmdb.api.MmdbApi;
import org.mymediadb.api.mmdb.internal.api.MmdbApiImpl;
import org.mymediadb.api.mmdb.model.User;

import java.awt.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

public class FidoCommonUtils {
    private static final Logger LOG = Logger.getLogger(FidoCommonUtils.class);
    private static Properties props = new Properties();
    public static String fidoMmdbPassword = "fido.mmdb.password";

    static {

        loadAdditionalPropertiesFromFile("fido");


    }


    public static boolean doesImdbIdExistsInTorrentDetails(Torrent torrent, String imdbId) {

        String page = getWebPage(torrent.getDetailsPageUrl(), torrent.getCookieString());

        return page.contains(imdbId);
    }


    public static double getVersion() {
        return 0.4;
    }

    /**
     * @return a List of all the Torrent Site Classes
     */
    public static List<Class> getAllTorrentSiteClasses() {

        List<Class> torrentSites = new ArrayList<Class>();

        Class<? extends TorrentSite>[] classList = ClassDiscovery.DiscoverClasses(TorrentSite.class, null, TorrentSite.class);
        for (Class<? extends TorrentSite> aClass : classList) {
            if (aClass.equals(TorrentSite.class) || aClass.equals(CookiedTorrentSite.class)) {
                continue;
            }
            if (TorrentSite.class.isAssignableFrom(aClass)) {
                torrentSites.add(aClass);
            }

        }
        return torrentSites;
    }

    public static File getSettingsDirectory() {
        String userHome = System.getProperty("user.home");
        if (userHome == null) {
            throw new IllegalStateException("user.home==null");
        }
        File home = new File(userHome);
        File settingsDirectory = new File(home, ".fido");
        if ((!settingsDirectory.exists()) && (!settingsDirectory.mkdir())) {
            throw new IllegalStateException(settingsDirectory.toString());
        }
        return settingsDirectory;
    }


    public static void loadAdditionalPropertiesFromFile(String settingsName) {

        FileInputStream fis = null;

        try {
            LOG.debug("LOADING PROPERTIES!");
            File settingsDirectory = getSettingsDirectory();
            File settingsFile = new File(settingsDirectory + File.separator + settingsName + ".properties");

            fis = new FileInputStream(settingsFile);
            //load all settings to memory
            props.load(fis);
            //unscramble the password
            if (settingsName.equals("fido")) {
                props.setProperty(fidoMmdbPassword, scrambleOrUnscramblePassword(props.getProperty(fidoMmdbPassword)));
            }
        } catch (IOException e) {
            LOG.debug(e);
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }


    }


    public static String getPropertyValue(String key) {
        String propertyValue = props.getProperty(key);
        if (propertyValue == null) {
            //LOG.info("You are missing the property: " + key);
            return "";
        }
        return propertyValue;
    }


    public static File getSettingsFile() {
        File settingsDirectory = getSettingsDirectory();


        File settingsFile = new File(settingsDirectory + File.separator + "fido.properties");
        if (!settingsFile.exists()) {
            LOG.info("Missing settings file, please run fido -install.");
        }
        return settingsFile;
    }

    public static String readKeyboardInput() {
        BufferedReader br = new BufferedReader(
                new InputStreamReader(System.in));
        String temp = "";
        try {
            temp = br.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return temp;
    }


    public static void saveGeneralSettings(String username, String password, String location, String timer, String trackers) {
        //Save settings file
        //Properties newProperties = getProperties();

        Properties newProperties = new Properties();
        //Getting properties from file first
        try {
            FileInputStream fis = new FileInputStream(getSettingsFile());
            newProperties.load(fis);
        } catch (FileNotFoundException e) {
            LOG.debug("settings file not found");
        } catch (IOException e) {
            e.printStackTrace();
        }


        if (username != null) {
            newProperties.setProperty("fido.mmdb.username", username);
        }
        if (password != null) {
            newProperties.setProperty(fidoMmdbPassword, scrambleOrUnscramblePassword(password));
        }
        if (location != null) {
            newProperties.setProperty("fido.directory.torrents", location);
        }

        if (trackers != null) {
            newProperties.setProperty("fido.current.trackers", trackers);
        }
        if (timer != null) {
            newProperties.setProperty("fido.current.timer", timer);
        }

        saveSettingsFile("fido", newProperties);
    }

    public static String getSelectedTorrentSitesAsStringList() {
        return getPropertyValue("fido.current.trackers");
    }

    public static String scrambleOrUnscramblePassword(String pwd) {
        final int UPPER_LIMIT = 125;
        final int LOWER_LIMIT = 48;
        final int CHAR_MAP = 39;
        String scrambledPwd;
        if (pwd==null) {
            pwd="";
        }
        try {
            byte[] buffer = pwd.getBytes("ISO-8859-1");

            for (int iData = 0; iData < buffer.length; iData++) {
                int iCode = buffer[iData];
                if ((iCode >= LOWER_LIMIT) && (iCode <= UPPER_LIMIT)) {
                    iCode += CHAR_MAP;
                    if (iCode > UPPER_LIMIT) {
                        iCode = iCode - UPPER_LIMIT + LOWER_LIMIT - 1;
                    }
                    buffer[iData] = (byte) iCode;
                }
            }
            scrambledPwd = new String(buffer, "ISO-8859-1");


        } catch (java.io.UnsupportedEncodingException e) {
            LOG.debug("Unicode/ISO FuckUp!");
            System.exit(-1);
            scrambledPwd = "Fido didn't understand your password...";
        }
        return scrambledPwd;
    }


    public static void saveSettingsFile(String propertiesName, Properties newProperties) {
        File settingsDirectory = getSettingsDirectory();
        File settingsFile = new File(settingsDirectory + File.separator + propertiesName + ".properties");
        try {
            newProperties.store(new FileOutputStream(settingsFile), "Created today");
        } catch (IOException e) {
            LOG.fatal(e);
            throw new RuntimeException();
        }
        LOG.info("A settings file have been saved to: " + settingsFile.getPath());
    }

    public static String prepareMovieNameForUrl(String movieName, String charSet) {
        String newMovieName;
        //TODO  Strip  - and : and others from movie name before searching
        //newMovieName= newMovieName.replaceAll("'","")
        //newMovieName= newMovieName.replaceAll("-","+")%F8
        //newMovieName=  newMovieName.replaceAll("ø","ø");
        newMovieName = movieName.replaceAll(":", " ");
        newMovieName = newMovieName.replaceAll("'", "");   // Needed for movies like The warrior's way

        try {
            newMovieName = URLEncoder.encode(newMovieName, charSet);
        } catch (UnsupportedEncodingException e) {
            LOG.debug(e);
            LOG.info("Fido encountered and error with the movie name. Will try to continue");
        }
        return newMovieName;
    }


    public static File downloadFile(URL fileUrl, String pathAndFileName, String cookie) {
        File file = new File(pathAndFileName);
        if (file.exists()) {
            throw new IllegalArgumentException("A file by that name already exists: " + pathAndFileName);
        }
        try {
            // Connect to the web page
            HttpURLConnection conn = (HttpURLConnection) fileUrl.openConnection();
            if (cookie != null) {
                conn.setRequestProperty("Cookie", cookie);
            }

            conn.connect();

            // Connect to the CONTENT and set up a BufferedReader for it.
            InputStream is;
            is = conn.getInputStream();
            FileOutputStream fos;

            fos = new FileOutputStream(file);
            int oneChar, count = 0;
            while ((oneChar = is.read()) != -1) {
                fos.write(oneChar);
                count++;
            }
            is.close();
            fos.close();
            LOG.info(count + " byte(s) copied");
        } catch (IOException e) {
            throw new DownloadException(e.getMessage());
        }

        return file;
    }

    public static String getWebPage(String url) {
        return FidoCommonUtils.getWebPage(url, "");
    }

    public static String getWebPage(String url, String cookie) {
        LOG.debug("Accessing page: " + url);

        // Remove bad characters

        String newUrl = url.replaceAll("'", ""); //


        // This will hold the HTML file
        StringBuilder text = new StringBuilder();
        newUrl = newUrl.replaceAll(" ", "%20");
        try {


            URL page = new URL(newUrl);
            // Connect to the web page
            HttpURLConnection conn = (HttpURLConnection) page.openConnection();
            if (cookie != null && !cookie.equals("")) {
                conn.setRequestProperty("Cookie", cookie);
            }
            conn.connect();

            // Connect to the CONTENT and set
            // up a BufferedReader for it.
            InputStream inputStream = conn.getInputStream();
            InputStreamReader in = new InputStreamReader(inputStream);
            BufferedReader read = new BufferedReader(in);

            // Now actually read the HTML
            String line;
            do {
                line = read.readLine();
                if (line != null) {
                    text.append(line);
                    text.append("\n");
                }
            } while (line != null);

        } catch (IOException e) {
            return "Error - :" + e.getMessage();
        }

        // Changes the StringBuilder to a String
        return text.toString();
    }

    public static boolean checkUsernameAndPassword(String userN, String passW) {
        MmdbApi mmdbApi = MmdbApiImpl.getInstance();
        mmdbApi.setBasicAuthentication(userN, passW);
        User mmdbUser = mmdbApi.getUser();
        if (mmdbUser == null) {
            LOG.info("Failed login at Mymediadb.org , please provide correct username/password");
            return false;
        }
        return true;
    }


    public static void setProperty(String key, String value) {
        props.setProperty(key, value);
    }

    public static boolean executeFile(File torrentFile) {
        //Todo consider if we really should have this function as it is probably not portable....
        Desktop desktop;
        if (Desktop.isDesktopSupported()) {
            desktop = Desktop.getDesktop();
            try {
                desktop.edit(torrentFile);
            } catch (IOException e) {
                return false;
            }
            return true;
        }
        return false;
    }


}
